///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once


#ifndef __Skeleton_H__
#define __Skeleton_H__

#include "OgrePrerequisites.h"
#include "OgreResource.h"
#include "math/quaternion.h"
#include "math/vector3.h"
#include "OgreIteratorWrappers.h"
#include "OgreStringVector.h"
#include "OgreAnimation.h"

namespace Ogre {
	
	

    
    enum SkeletonAnimationBlendMode {
        /// Animations are applied by calculating a weighted average of all animations
	    ANIMBLEND_AVERAGE = 0,
        /// Animations are applied by calculating a weighted cumulative total
	    ANIMBLEND_CUMULATIVE = 1
    };

#define OGRE_MAX_NUM_BONES 256

	
	struct LinkedSkeletonAnimationSource;

    
    class _OgreExport Skeleton : public Resource, public AnimationContainer
    {
		friend class SkeletonInstance;
	protected:
		/// Internal constructor for use by SkeletonInstance only
		Skeleton();

    public:
        
        Skeleton(ResourceManager* creator, const String& name, ResourceHandle handle,
            const String& group, bool isManual = false, ManualResourceLoader* loader = 0);
        virtual ~Skeleton();


        
        virtual Bone* createBone(void);

        
        virtual Bone* createBone(unsigned short handle);

        
        virtual Bone* createBone(const String& name);

        
        virtual Bone* createBone(const String& name, unsigned short handle);

        
        virtual unsigned short getNumBones(void) const;

        
        virtual Bone* getRootBone(void) const;

        typedef vector<Bone*>::type BoneList;
        typedef VectorIterator<BoneList> BoneIterator;
        /// Get an iterator over the root bones in the skeleton, ie those with no parents
        virtual BoneIterator getRootBoneIterator(void);
        /// Get an iterator over all the bones in the skeleton
        virtual BoneIterator getBoneIterator(void);

        
        virtual Bone* getBone(unsigned short handle) const;

        
        virtual Bone* getBone(const String& name) const;

		
        virtual bool hasBone(const String& name) const;

        
        virtual void setBindingPose(void);

        
        virtual void reset(bool resetManualBones = false);

        
        virtual Animation* createAnimation(const String& name, Real length);

        
        virtual Animation* getAnimation(const String& name, 
			const LinkedSkeletonAnimationSource** linker) const;

		
		virtual Animation* getAnimation(const String& name) const;

		/// Internal accessor for animations (returns null if animation does not exist)
		virtual Animation* _getAnimationImpl(const String& name, 
			const LinkedSkeletonAnimationSource** linker = 0) const;


		
		virtual bool hasAnimation(const String& name) const;

        
        virtual void removeAnimation(const String& name);

        
        virtual void setAnimationState(const AnimationStateSet& animSet);


        
        virtual void _initAnimationState(AnimationStateSet* animSet);

		
		virtual void _refreshAnimationState(AnimationStateSet* animSet);

		
        virtual void _getBoneMatrices(Matrix4* pMatrices);

        
        virtual unsigned short getNumAnimations(void) const;

        
        virtual Animation* getAnimation(unsigned short index) const;


		
        virtual SkeletonAnimationBlendMode getBlendMode() const;
        
		virtual void setBlendMode(SkeletonAnimationBlendMode state);

        /// Updates all the derived transforms in the skeleton
        virtual void _updateTransforms(void);

		
		virtual void optimiseAllAnimations(bool preservingIdentityNodeTracks = false);

		
		virtual void addLinkedSkeletonAnimationSource(const String& skelName, 
			Real scale = 1.0f);
		/// Remove all links to other skeletons for the purposes of sharing animation
		virtual void removeAllLinkedSkeletonAnimationSources(void);
		
		typedef vector<LinkedSkeletonAnimationSource>::type 
			LinkedSkeletonAnimSourceList;
		typedef ConstVectorIterator<LinkedSkeletonAnimSourceList> 
			LinkedSkeletonAnimSourceIterator;
		/// Get an iterator over the linked skeletons used as animation sources
		virtual LinkedSkeletonAnimSourceIterator 
			getLinkedSkeletonAnimationSourceIterator(void) const;

		/// Internal method for marking the manual bones as dirty
		virtual void _notifyManualBonesDirty(void);
		/// Internal method for notifying that a bone is manual
		virtual void _notifyManualBoneStateChange(Bone* bone);

		/// Have manual bones been modified since the skeleton was last updated?
		virtual bool getManualBonesDirty(void) const { return mManualBonesDirty; }
		/// Are there any manually controlled bones?
		virtual bool hasManualBones(void) const { return !mManualBones.empty(); }

        /// Map to translate bone handle from one skeleton to another skeleton.
        typedef vector<ushort>::type BoneHandleMap;

        
        virtual void _mergeSkeletonAnimations(const Skeleton* source,
            const BoneHandleMap& boneHandleMap,
            const StringVector& animations = StringVector());

        
        virtual void _buildMapBoneByHandle(const Skeleton* source,
            BoneHandleMap& boneHandleMap) const;

        
        virtual void _buildMapBoneByName(const Skeleton* source,
            BoneHandleMap& boneHandleMap) const;

	protected:
		SkeletonAnimationBlendMode mBlendState;
        /// Storage of bones, indexed by bone handle
        BoneList mBoneList;
        /// Lookup by bone name
        typedef map<String, Bone*>::type BoneListByName;
        BoneListByName mBoneListByName;


        /// Pointer to root bones (can now have multiple roots)
        mutable BoneList mRootBones;
        /// Bone automatic handles
        unsigned short mNextAutoHandle;
		typedef set<Bone*>::type BoneSet;
		/// Manual bones
		BoneSet mManualBones;
		/// Manual bones dirty?
		bool mManualBonesDirty;


        /// Storage of animations, lookup by name
        typedef map<String, Animation*>::type AnimationList;
        AnimationList mAnimationsList;

		/// List of references to other skeletons to use animations from 
		mutable LinkedSkeletonAnimSourceList mLinkedSkeletonAnimSourceList;

        
        void deriveRootBone(void) const;

        /// Debugging method
        void _dumpContents(const String& filename);

        
        void loadImpl(void);

        
        void unloadImpl(void);
		/// @copydoc Resource::calculateSize
		size_t calculateSize(void) const { return 0; } // TODO 

    };

    
    class _OgreExport SkeletonPtr : public SharedPtr<Skeleton> 
    {
    public:
        SkeletonPtr() : SharedPtr<Skeleton>() {}
        explicit SkeletonPtr(Skeleton* rep) : SharedPtr<Skeleton>(rep) {}
        SkeletonPtr(const SkeletonPtr& r) : SharedPtr<Skeleton>(r) {} 
        SkeletonPtr(const ResourcePtr& r) : SharedPtr<Skeleton>()
        {
			// lock & copy other mutex pointer
            OGRE_MUTEX_CONDITIONAL(r.OGRE_AUTO_MUTEX_NAME)
            {
			    OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
			    OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
                pRep = static_cast<Skeleton*>(r.getPointer());
                pUseCount = r.useCountPointer();
                if (pUseCount)
                {
                    ++(*pUseCount);
                }
            }
        }

        /// Operator used to convert a ResourcePtr to a SkeletonPtr
        SkeletonPtr& operator=(const ResourcePtr& r)
        {
            if (pRep == static_cast<Skeleton*>(r.getPointer()))
                return *this;
            release();
			// lock & copy other mutex pointer
            OGRE_MUTEX_CONDITIONAL(r.OGRE_AUTO_MUTEX_NAME)
            {
			    OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
			    OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
                pRep = static_cast<Skeleton*>(r.getPointer());
                pUseCount = r.useCountPointer();
                if (pUseCount)
                {
                    ++(*pUseCount);
                }
            }
			else
			{
				// RHS must be a null pointer
				assert(r.isNull() && "RHS must be null if it has no mutex!");
				setNull();
			}
            return *this;
        }
    };

	/// Link to another skeleton to share animations
	struct LinkedSkeletonAnimationSource
	{
		String skeletonName;
		SkeletonPtr pSkeleton;
		Real scale;
		LinkedSkeletonAnimationSource(const String& skelName, Real scl)
			: skeletonName(skelName), scale(scl) {}
			LinkedSkeletonAnimationSource(const String& skelName, Real scl, 
				SkeletonPtr skelPtr)
				: skeletonName(skelName), pSkeleton(skelPtr), scale(scl) {}
	};
	
	

}


#endif

